package com.lj.back.manage.scaffold.config;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.ClassScanner;
import com.lj.back.manage.scaffold.BackManageScaffoldApplication;
import com.lj.back.manage.scaffold.common.constant.Constant;
import com.lj.back.manage.scaffold.common.exception.MyException;
import com.lj.back.manage.scaffold.common.response.ResponseCodeConstant;
import com.lj.back.manage.scaffold.module.job.DO.ScheduleJobDO;
import com.lj.back.manage.scaffold.module.job.DTO.ScheduleJobDTO;
import com.lj.back.manage.scaffold.module.job.annotation.QuartzTask;
import com.lj.back.manage.scaffold.module.job.dao.ScheduleJobDao;
import com.lj.back.manage.scaffold.module.job.enums.ScheduleJobOwnerEnum;
import com.lj.back.manage.scaffold.module.job.enums.ScheduleStatusEnum;
import com.lj.back.manage.scaffold.module.job.utils.ScheduleUtils;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.quartz.Scheduler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;

import javax.annotation.PostConstruct;
import java.lang.reflect.Method;
import java.util.*;

/**
 * 定时任务配置
 * @author lv
 */
@Configuration
@Slf4j
@AllArgsConstructor
public class ScheduleConfig {

    private final SchedulerFactoryBean schedulerFactoryBean;

    private final ScheduleJobDao scheduleJobDao;

    @Bean
    public Scheduler scheduler(){
        return schedulerFactoryBean.getScheduler();
    }

    /**
     * 初始化定时任务 分为两步  一更新数据库定时任务信息   二根据数据库定时任务信息创建或更新定时任务
     * 一、更新数据库定时任务信息
     * 1、首先从spring容器中获取所有包含{@link QuartzTask}注解的方法
     * 2、根据QuartzTask属性判断数据库是否存在该定时任务
     * 3、如果不存在则往数据库新增定时任务信息
     * 4、如果存在则判断该定时任务的拥有者是否是CODER
     * 5、如果是则根据QuartzTask注解信息更新数据库信息
     * 6、如果不是则不作任何操作
     * 二、根据数据库定时任务信息创建或更新定时任务
     * 1、判断数据库中该定时任务在代码中是否存在
     * 2、如果不存在则删除数据库定时任务信息
     * 3、如果存在则创建或者更新定时任务
     */
    @PostConstruct
    public void scan(){
        // TODO 如果修改包名  请修改
        Set<Class<?>> classes = ClassScanner.scanPackage(BackManageScaffoldApplication.class.getPackage().getName());
        //一、更新数据库定时任务信息
        List<String> beans = new ArrayList<>();
        classes.forEach(taskClass->{
            Method[] methods = taskClass.getDeclaredMethods();
            Arrays.stream(methods).forEach(method -> {
                method.setAccessible(true);
                QuartzTask quartzTask = method.getAnnotation(QuartzTask.class);
                if (quartzTask != null){
                    long id = quartzTask.id();
                    String className = taskClass.getName();
                    beans.add(className+ Constant.SEPARATOR_BEAN_NAME+method.getName());
                    ScheduleJobDO scheduleJobDO = scheduleJobDao.selectById(id);
                    String bean = className+Constant.SEPARATOR_BEAN_NAME+method.getName();
                    //新增或更新
                    if (scheduleJobDO == null){
                        //新增
                        scheduleJobDO = new ScheduleJobDO();
                        scheduleJobDO.setId(quartzTask.id());
                        scheduleJobDO.setRemark(quartzTask.remark());
                        scheduleJobDO.setCron(quartzTask.cron());
                        scheduleJobDO.setName(quartzTask.name());
                        scheduleJobDO.setBean(bean);
                        scheduleJobDO.setStatus(ScheduleStatusEnum.NORMAL.getCode());
                        scheduleJobDO.setOwner(ScheduleJobOwnerEnum.CODER.getCode());
                        scheduleJobDao.insert(scheduleJobDO);
                    }else {
                        // 判断定时任务拥有者
                        if (Objects.equals(ScheduleJobOwnerEnum.CODER.getCode(), scheduleJobDO.getOwner())){
                            if (!Objects.equals(scheduleJobDO.getBean(),bean)){
                                throw new MyException(ResponseCodeConstant.UNKNOWN, "定时任务ID冲突,正在使用的类："+scheduleJobDO.getBean()+"冲突的类："+className);
                            }
                            scheduleJobDO.setRemark(quartzTask.remark());
                            scheduleJobDO.setCron(quartzTask.cron());
                            scheduleJobDO.setName(quartzTask.name());
                            // 更新定时任务
                            scheduleJobDao.updateById(scheduleJobDO);
                        }
                    }
                }
            });
        });

        //二、根据数据库定时任务信息创建或更新定时任务
        List<ScheduleJobDO> scheduleJobDOS = scheduleJobDao.selectList(null);
        scheduleJobDOS.forEach(scheduleJobDO -> {
            //移除定时任务
            if (!beans.contains(scheduleJobDO.getBean())){
                scheduleJobDao.deleteById(scheduleJobDO.getId());
                scheduleJobDOS.remove(scheduleJobDO);
            }else {
                //创建定时任务
                ScheduleUtils.createScheduleJob(scheduler(),BeanUtil.copyProperties(scheduleJobDO, ScheduleJobDTO.class));
            }
        });
    }
}
